#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_EXT_scalar_block_layout : enable
#extension GL_EXT_nonuniform_qualifier : enable
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_buffer_reference2 : require
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#extension GL_ARB_shader_clock : enable
#extension GL_EXT_shader_16bit_storage : enable



#include "../../../../core/shader_std.h"
#include "../../../../core/shader_built_in_functions.h"
#include "../../../../core/shader_PBR.h"
#include "../../../../core/shader_ob.h"
#include "ray_std.h"



//#extension GL_EXT_debug_printf : enable
//debugPrintfEXT("Test");

//模型顶点类型
layout(buffer_reference, scalar) readonly buffer B_Vertices {S_Vert V[]; }; 
//layout(buffer_reference, scalar) readonly buffer B_Faces    {S_FaceElement Element[]; };



//全局统一数据类型
//layout(buffer_reference, scalar, std430) readonly buffer B_MaterialRef {S_Material M[]; };
//layout(binding = DEF_ObMeshBindID, scalar) readonly buffer B_ObjMesh { S_ObMesh m_Address[];} ObMesh;

layout(binding = 0, set = 0) uniform accelerationStructureEXT topLAS;
layout(binding = 2, set = 0) uniform CameraProp {
	mat4 projInverse;
	mat4 viewInverse;
	mat4 projMat;
	mat4 viewMat;
} cam;

layout(binding = DEF_RenderFrameBindID, set = 0, scalar) uniform FrameProp {
	GLSL_RenderFrame renderFrame;
};


layout(location = 0) rayPayloadInEXT S_Ray hitValue;
layout(location = 3) callableDataEXT S_SceneDataCall callSceneData;
//layout(location = 1) callableDataEXT S_Material callMaterial;

hitAttributeEXT vec2 attribs;
//gl_GeometryIndexEXT
//gl_InstanceCustomIndexEXT

void main(){
	if(hitValue.Depth > 8 || hitValue.RayType == DEF_LigthReturnRayType) return; 

	uint GeometryIndex = gl_InstanceCustomIndexEXT & 0x0000ffff;
	//uint GeometryIndex = 1;

	uint flags = gl_RayFlagsOpaqueEXT;
	uint cullMask = 0xff;
	float tmin = 0.0001;
	float tmax = 1000.0;
	vec3 origin = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT;

	uint seed = f_tea(gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x, int(clockARB()) ); 
	//uint seed = f_tea(gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x, 1); 
	


	S_ObMesh    Resource_ObMesh = ObMesh.m_Address[GeometryIndex];
	B_Indexs    mesh_index      = B_Indexs(Resource_ObMesh.m_AddressIndex);
	B_Vertices  mesh_vert       = B_Vertices(Resource_ObMesh.m_AddressVertex);

	uint OBType   = Resource_ObMesh.m_ObType;

	uvec3       vertIndex = mesh_index.I[gl_PrimitiveID];
	vec3        v0 = mesh_vert.V[vertIndex.x].vert;
	vec3        v1 = mesh_vert.V[vertIndex.y].vert;
	vec3        v2 = mesh_vert.V[vertIndex.z].vert;
	
	vec3        barycentrics = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y);

	vec3 vert = v0 * barycentrics.x + v1 * barycentrics.y + v2 * barycentrics.z;

	vec3 normal = mesh_vert.V[vertIndex.x].normal * barycentrics.x + mesh_vert.V[vertIndex.y].normal * barycentrics.y + mesh_vert.V[vertIndex.z].normal * barycentrics.z;
	//normal = normalize(mat3(gl_ObjectToWorldEXT) * normal);
	normal = normalize(vec3(normal * gl_WorldToObjectEXT));
	
	vec3 sidedNormal = normal;
	//if(dot(gl_WorldRayDirectionEXT, normal) >= 0) sidedNormal = -sidedNormal;
	faceforward(sidedNormal, normal, gl_WorldRayDirectionEXT);
	
	
	//v0 = vec3(v0 * gl_WorldToObjectEXT);
	//v1 = vec3(v1 * gl_WorldToObjectEXT);
	//v2 = vec3(v2 * gl_WorldToObjectEXT);
	vec3 faceNormal = cross(normalize(v0 - v1), normalize(v1 - v2));
	if(dot(faceNormal, gl_WorldRayDirectionEXT) >= 0){
		normal = -normal;
	}else{
		normal = normal;
	}
	//sidedNormal = normal;
	hitValue.Normal = sidedNormal;
	
	
	//模型顶点元素数据
	//B_UV       uv_coord   = B_UV(Resource_ObMesh.m_AddressUV);
	//B_Indexs   uv_index   = B_Indexs(Resource_ObMesh.m_AddressUVIndex);
	B_Faces    mesh_face  = B_Faces(Resource_ObMesh.m_AddressFaces);
	//面元素
	S_FaceElement Element = mesh_face.Element[gl_PrimitiveID];
	//索引材质
	S_Material material = m_Material[ Element.MaterialID ];
	


	//hitDis += hitValue.Dis;
	//callSceneData.dis = hitValue.Dis;
	//callSceneData.GeometryIndex = gl_GeometryIndexEXT;
	callSceneData.reflect = material.m_reflect - 0.0000001;
	callSceneData.rayDir = gl_WorldRayDirectionEXT;
	callSceneData.PrimitiveIndex = gl_PrimitiveID;
	callSceneData.InstanceIndex  = GeometryIndex;
	callSceneData.alpha          = 1;
	callSceneData.MaterialID   = Element.MaterialID;

	//hitValue.Dis += gl_HitTEXT;
	//float hitDis = 1;
	float irradiance = 1;// = normalize(max(dot(irradianceDir, normal), 0));
	float transmission = material.m_transmission;
	uint Depth = hitValue.Depth;
	++hitValue.Depth;
	
	
	//vec2 texCoord = v0.texCoord * barycentrics.x + v1.texCoord * barycentrics.y + v2.texCoord * barycentrics.z;
	//float roughness = 1;
	vec3 irradianceDir;// = normalize((f_SphereSampling_toWorld(TangentSample, normal) + (rv * (1-roughness) * 60) ));
	//float reflectIntensity =  material.m_refract;
	float reflectIntensity =  0;
	uint mask = 0;

	if(hitValue.Depth < 8) {
		switch(OBType){
			case DEF_ObType_Ligth : {
				
				R_GlobalParameter_LigthArea LigthArea_GParam = R_GlobalParameter_LigthArea(gGlobalParam.m_AddressLigthArea);
				S_LigthArea LigthArea = LigthArea_GParam.LigthAreaPtr[gl_InstanceCustomIndexEXT >> 16];

				mask = LigthArea.m_mask;
				if ( (hitValue.LigthMask & LigthArea.m_mask) != 0  && LigthArea.m_enable != 0) {
					float color_dot = max(dot(normal, -gl_WorldRayDirectionEXT), 0);

					irradianceDir = gl_WorldRayDirectionEXT;
					hitValue.Color += LigthArea.m_intensity * sign(color_dot) * hitValue.Alpha;
					//hitValue.Color += LigthArea.m_intensity * sign(color_dot);
					irradiance = color_dot;
					hitValue.irradiance += LigthArea.m_intensity * sign(color_dot) * hitValue.Alpha;
					//callSceneData.Color = vec3(1);
				} else {
					callSceneData.Color = vec3(0.001);
					//callSceneData.reflect = 0.001;
					
				}
				//callSceneData.alpha = 0;
				hitValue.Alpha = 0;
				break;
			}
		}
	}
	
	
	
	callSceneData.MaterialID   = Element.MaterialID;
	callSceneData.Vert         = vert;
	callSceneData.irradiance   = hitValue.irradiance;
	callSceneData.barycentrics = barycentrics;
	callSceneData.roughness    = material.m_roughness;
	callSceneData.Normal       = sidedNormal;
	//hitValue.Normal            = normal;

	if(material.m_shaderIndex >= 0) {
		executeCallableEXT(material.m_shaderIndex, 3);
	}else{
		callSceneData.Color += vec3(0.1);
	}


	/*switch(gl_GeometryIndexEXT){
		case 0: callSceneData.Color = vec3(material.m_roughness, 0, 0); break;
		case 1: callSceneData.Color = vec3(0, 2, 0); break;
		case 2: callSceneData.Color = vec3(0, 0, 2); break;  + int(clockARB()) 
	}*/
	
	vec3 color = callSceneData.Color + 0.00001;
	
	vec2 noise = vec2(f_rnd(seed)*6.283185, f_rnd(seed + int(clockARB()) )*1.570796*callSceneData.roughness);
	vec3 TangentSample = normalize(f_SphereSampling(noise));

	//bool external = true;
	switch(OBType){
		case DEF_ObType_Mesh : {
			float random = f_rnd( seed );
			
			if ( random >= callSceneData.alpha ) {
				irradianceDir = gl_WorldRayDirectionEXT;
				irradiance = 1;
				color = vec3(0);
			}else{
				if ( random >= transmission ) {
					vec3 rv = reflect(gl_WorldRayDirectionEXT, sidedNormal);
					irradianceDir = normalize(f_SphereSampling_toWorld(TangentSample, rv));
					
				} else {
					vec3 rv = refract(gl_WorldRayDirectionEXT, normal, material.m_refract);
					irradianceDir = normalize(f_SphereSampling_toWorld(TangentSample, rv));
				}
				
				irradiance = clamp(abs(dot(irradianceDir, sidedNormal)), 0.001, 1);
			}
			
			
			//irradianceDir= mix(irradianceDir, gl_WorldRayDirectionEXT, transmission);
			hitValue.LigthMask &= material.m_LigthMask;
			//hitValue.irradiance *= irradiance;
			//hitValue.Dir = irradianceDir;
			if(Depth > 0){
				//hitValue.irradiance /= (1 / (gl_HitTEXT+0.0001));
				//hitDis += gl_HitTEXT * hitValue.Alpha;
				hitValue.LigthDis += gl_HitTEXT * hitValue.Alpha;
			}

			hitValue.Alpha = callSceneData.alpha;
			break;
		}
	default:
		hitValue.Alpha = 0;
	}
	
	
	
	if(hitValue.Depth < 8) {
		traceRayEXT(topLAS, flags, cullMask, 0, 0, 0, origin, tmin, irradianceDir, tmax, 0);

		//if(hitValue.Depth == 64){
		//	callSceneData.rayDir = gl_WorldRayDirectionEXT;
		//	executeCallableEXT(renderFrame.m_world_ShaderID, 3);
		//	
		//	vec3 w = vec3(0.2125,0.7154,0.0721);
		//	float luminance = max(dot(callSceneData.Color, w), 0);
		//	hitValue.Color += callSceneData.Color;
		//	hitValue.irradiance += luminance;
		//}
		
		vec3 w = vec3(0.2125,0.7154,0.0721);
		float luminance = dot(w, color);
		//color += hitValue.Color / (luminance+1);
		//color += hitValue.Color * (1-callSceneData.roughness);
		color += hitValue.Color * irradiance * ((luminance + 0.5) * 0.75);
		//color += hitValue.Color;
		//color *= normalize(color) * 0.5;
		//color /= hitDis;
		//color *= irradiance;
		//color *= hitValue.irradiance;
		//hitDis = gl_HitTEXT;
		//hitValue.irradiance /= hitDis;
		//color *= (irradiance + callSceneData.roughness) * 0.5;

		//hitValue.Color = mix(color, hitValue.Color, transmission);
		hitValue.Color = mix(hitValue.Color, color, callSceneData.alpha);
	
	}
	
	if(hitValue.Depth >= 8) {
		
		hitValue.RayType = DEF_LigthReturnRayType;
		
		R_GlobalParameter_LigthPoint LigthPoint_GParam = R_GlobalParameter_LigthPoint(gGlobalParam.m_AddressLigthPoint);
		R_GlobalParameter_LigthSpot LigthSpot_GParam = R_GlobalParameter_LigthSpot(gGlobalParam.m_AddressLigthSpot);
		
		vec3 sphereSampling = f_SphereSampling_toWorld(TangentSample, sidedNormal);
		
		for(uint i=0; i<renderFrame.m_PointLightNum; ++i){
			S_LigthPoint light = LigthPoint_GParam.LigthPointPtr[i];
			
			if( (hitValue.LigthMask & light.m_mask) != 0 && light.m_enable != 0) {
				vec3 ligthDir = light.m_pos - origin;
				float ligthArea = min(length(ligthDir), light.m_falloff);

				hitValue.Dis = ligthArea;
				ligthDir = normalize(ligthDir);
				hitValue.Ligth_Type_ID = ((DEF_ObType_Ligth_Point<<16) | i);
				callSceneData.ligthDir = ligthDir;
				
				traceRayEXT(topLAS, flags, cullMask, 0, 0, 0, origin + sphereSampling * 0.01, 0.0001, ligthDir, ligthArea, 0);
			}
		}
	}
	
}






	//hitValue.Color = mix(color * (hitValue.irradiance), hitValue.Color, callSceneData.roughness - transmission);
	//hitValue.Color = mix(color * hitValue.irradiance, hitValue.Color, 0.5);
	//hitValue.Color = mix(color * hitValue.irradiance, hitValue.Color, callSceneData.roughness);
	//hitValue.Color = mix(color * hitValue.irradiance, hitValue.Color, callSceneData.roughness);
	//hitValue.Color = mix(color * hitValue.irradiance, hitValue.Color, (1-transmission) );
	//hitValue.Color = mix(hitValue.Color, color * hitValue.irradiance, 0.5);
	//hitValue.Color += abs(color * hitValue.irradiance * (callSceneData.roughness - transmission));
	//hitValue.Color += abs((color+hitValue.Color) * 0.1 * hitValue.irradiance * (callSceneData.roughness - transmission));
	//hitValue.Color += ((color+hitValue.Color) * 0.1 * hitValue.irradiance * (callSceneData.roughness));
	//hitValue.Color += ((color + hitValue.Color) * irradiance * 0.1 * (1 - callSceneData.roughness));
	//hitValue.Color += ((color) * irradiance * max(min(callSceneData.roughness, 1), 0) );
	//hitValue.Color += (color * irradiance * hitValue.irradiance * callSceneData.roughness);
	//callSceneData.roughness = 1;

	//hitValue.Color *= luminance;
	//hitValue.Color += (color * hitValue.irradiance * callSceneData.roughness) * callSceneData.alpha;

	
	
	//vec3 previous = normalize(mix(color, hitValue.Color, luminancePrevious * callSceneData.reflect ));
	
	
	//previous += (color * callSceneData.roughness); 
	//hitValue.LigthMask &= material.m_LigthMask;
	//if((hitValue.LigthMask) > 0){
	//if((hitValue.LigthMask & material.m_LigthMask) > 0){
		
	//}else{
	//	previous = color;
	//	previous *= irradiance;
	//}

	
	
	
	//color += hitValue.Color;
	//color *= luminance * hitValue.irradiance;
	//previous = mix(color, hitValue.Color, (callSceneData.reflect + luminance - callSceneData.alpha) * (1-callSceneData.roughness) );
	//hitValue.Color = mix(color, hitValue.Color, callSceneData.alpha);
	//previous += hitValue.Color * callSceneData.roughness * luminance;
	//hitValue.Color = color * hitValue.irradiance;
	//hitValue.Color += (color * callSceneData.roughness);
	//hitValue.Color *= luminance;
	//color *= luminance * hitValue.irradiance;
	//hitValue.Color = mix(color, hitValue.Color, (luminance + reflectIntensity) * 0.5);
	//hitValue.Color += color * 0.1;
	//hitValue.Color *= luminance;
	//hitValue.Color *= hitValue.irradiance;
	//hitValue.Color = (color * hitValue.irradiance * callSceneData.roughness);
	//hitValue.Color += (color);
	//hitValue.Color += (color * callSceneData.roughness);
	//hitValue.Color = mix(hitValue.Color, color * hitValue.irradiance, callSceneData.roughness);
	//hitValue.Color = mix(hitValue.Color, (hitValue.Color + color) * irradiance * hitValue.irradiance, callSceneData.roughness);
	//hitValue.Color = mix(hitValue.Color, (color) * hitValue.irradiance, callSceneData.roughness);
	//hitValue.Color = mix((color+hitValue.Color) * 0.1, hitValue.Color, (1 - callSceneData.roughness));
	//hitValue.Color = mix(color * hitValue.irradiance, hitValue.Color, (callSceneData.roughness));
	//hitValue.Color = mix(hitValue.Color, color * hitValue.irradiance, 1 - transmission);



		//hitValue.irradiance /= hitDis;
		
		//float luminance = clamp(dot(w, (color)), callSceneData.roughness, 1);
		
		//float luminancePrevious = (dot(w, hitValue.Color)); 
		//vec3 previous;
		//color += hitValue.Color * (callSceneData.roughness + 0.5) * 0.5;
		//color *= hitValue.irradiance;
		
		
		//color = 1 - ((1 - color * 0.5) * 1);
		//color += hitValue.Color;
		
		//color *= color;
		//color *= clamp(luminance, 0.1, 1);
		//hitValue.Color = color * 0.1;
		
		//color = mix(hitValue.Color, color * hitValue.irradiance, irradiance);
		//color *= (luminance+0.1 + transmission) * (hitValue.irradiance * 0.7);
		//color *= hitValue.irradiance;
		
		//hitValue.Color = mix(hitValue.Color, color * 0.1, callSceneData.alpha);
		//hitValue.Color *= irradiance;
		//color *= irradiance;
		
		//color *= irradiance /= hitDis;
		//previous = color;
		//previous += hitValue.Color * hitValue.irradiance;
		//previous = normalize(mix(color, hitValue.Color * luminance, min(abs(callSceneData.reflect + transmission) * 0.3, 1) ));
		//previous = (mix(color, hitValue.Color, min(abs(transmission), 1) ));
		//previous = mix(color, hitValue.Color, min(abs(transmission), 1) );
		//previous *= (hitValue.irradiance * luminance) * 0.5;
		//previous *= (luminance * irradiance * 0.5) * 0.5;
		//previous += hitValue.Color;
		//previous *= hitValue.irradiance;
		//previous *= (luminance+0.1)*0.9;
		//previous = vec3(transmission);
		//previous /= hitDis;
		//hitValue.Color = mix(previous, hitValue.Color, transmission);
		//hitValue.Color = mix(hitValue.Color, previous, callSceneData.alpha);
		
		//hitValue.Color = mix(hitValue.Color, color, 0);
		//hitValue.Color = mix(hitValue.Color * irradiance, previous * hitValue.irradiance, callSceneData.alpha);



		
				//vec3 rv = refract(gl_WorldRayDirectionEXT, normal, material.m_refract);
				//irradianceDir = normalize((f_SphereSampling_toWorld(TangentSample, sidedNormal) + (rv * (1-callSceneData.roughness)) ));


				//irradianceDir = normalize((f_SphereSampling_toWorld(TangentSample, sidedNormal) + (rv * (1-callSceneData.roughness) * 30) ));
				//irradianceDir = normalize((f_SphereSampling_toWorld(TangentSample, sidedNormal) + (rv) ));

